package com.leilei.sensitive;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import lombok.Setter;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.context.ApplicationContext;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: lei
 * @date: 2025-05-23 9:36
 * @desc: 逆脱敏 mybatisPlus拦截器
 */
@Setter
@Intercepts({
        @Signature(
                type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class ReverseSensitiveMpInterceptor implements Interceptor {

    private ApplicationContext applicationContext;

    private static final Map<Class<?>, BaseMapper<?>> MAPPER_CACHE = new ConcurrentHashMap<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 只处理 UPDATE 操作
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        if (!SqlCommandType.UPDATE.equals(ms.getSqlCommandType())) {
            return invocation.proceed();
        }
        Object param = invocation.getArgs()[1];
        Object entity = extractEntity(param);
        if (entity == null) {
            return invocation.proceed();
        }
        Class<?> entityClass = entity.getClass();
        List<Field> fields = ReverseSensitiveDataHandler.resolveReverseField(entityClass);
        if (fields.isEmpty()) {
            return invocation.proceed();
        }
        Object id = ReverseSensitiveDataHandler.extractIdValue(entity);
        if (id == null) {
            return invocation.proceed();
        }
        // 获取 Mapper 实例
        BaseMapper<?> mapper = getMapper(entityClass, ms);
        if (mapper == null) {
            return invocation.proceed();
        }
        Object original = mapper.selectById((Serializable) id);
        if (original == null) {
            return invocation.proceed();
        }
        ReverseSensitiveDataHandler.handleSensitiveData(original, entity);
        return invocation.proceed();
    }

    private Object extractEntity(Object paramObj) {
        if (paramObj instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) paramObj;
            for (Object val : map.values()) {
                if (val != null && TableInfoHelper.getTableInfo(val.getClass()) != null) {
                    return val;
                }
            }
        }
        return paramObj;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }


    @SuppressWarnings("unchecked")
    public <T> BaseMapper<T> getMapper(Class<T> entityClass, MappedStatement ms) {
        return (BaseMapper<T>) MAPPER_CACHE.computeIfAbsent(entityClass, cls -> {
            String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf('.'));
            try {
                Class<?> mapperInterface = Class.forName(mapperClassName);
                return (BaseMapper<?>) applicationContext.getBean(mapperInterface);
            } catch (Exception e) {
                return null;
            }
        });
    }

}
